/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.mapping;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;

/**
 * @author Clinton Begin
 */
/**
 * 结果映射 MyBatis 中最重要最强大的元素
 */
public class ResultMapping {
	/**
	 * 配置
	 */
	private Configuration configuration;
	/**
	 * java对象的属性名
	 */
	private String property;
	/**
	 * 结果集中的字段名
	 */
	private String column;
	/**
	 * 属性类型
	 */
	private Class<?> javaType;
	/**
	 * JDBC类型
	 */
	private JdbcType jdbcType;
	/**
	 * 类型处理器
	 */
	private TypeHandler<?> typeHandler;
	/**
	 * 内部映射的ResultMapId
	 */
	private String nestedResultMapId;
	/**
	 * 内查询ID
	 */
	private String nestedQueryId;
	/**
	 * 对应元素的notNullColumn属性拆分后的结果
	 */
	private Set<String> notNullColumns;
	/**
	 * 对应元素的columnPrefix属性
	 */
	private String columnPrefix;
	/**
	 * 处理后的标志，标志共两个：id和constructor
	 */
	private List<ResultFlag> flags;
	/**
	 * 对应元素的column属性拆分后生成的结果，composites.size()>0会时column为null
	 */
	private List<ResultMapping> composites;
	/**
	 * 对应元素的resultSet属性
	 */
	private String resultSet;
	/**
	 * 对应元素的foreignColumn属性
	 */
	private String foreignColumn;
	/**
	 * 是否延迟加载，对应元素的fetchType属性值，lazy则为true否则为false,也直接从配置对象中读取
	 */
	private boolean lazy;

	ResultMapping() {
	}

	/**
	 * 静态内部类，建造者模式
	 * 
	 * @author smilesnake
	 *
	 */
	public static class Builder {
		private ResultMapping resultMapping = new ResultMapping();

		/**
		 * 构造ResultMapping
		 * 
		 * @param configuration
		 * @param property
		 * @param column
		 * @param typeHandler
		 */
		public Builder(Configuration configuration, String property, String column, TypeHandler<?> typeHandler) {
			this(configuration, property);
			resultMapping.column = column;
			resultMapping.typeHandler = typeHandler;
		}

		/**
		 * 构造ResultMapping
		 * 
		 * @param configuration
		 * @param property
		 * @param column
		 * @param javaType
		 */
		public Builder(Configuration configuration, String property, String column, Class<?> javaType) {
			this(configuration, property);
			resultMapping.column = column;
			resultMapping.javaType = javaType;
		}
		/**
		 * 构造ResultMapping
		 * @param configuration
		 * @param property
		 */
		public Builder(Configuration configuration, String property) {
			resultMapping.configuration = configuration;
			resultMapping.property = property;
			resultMapping.flags = new ArrayList<ResultFlag>();
			resultMapping.composites = new ArrayList<ResultMapping>();
			resultMapping.lazy = configuration.isLazyLoadingEnabled();
		}
		/**
		 * 构建属性类型
		 * @param javaType
		 * @return
		 */
		public Builder javaType(Class<?> javaType) {
			resultMapping.javaType = javaType;
			return this;
		}
		/**
		 * 构建JDBC类型
		 * @param jdbcType
		 * @return
		 */
		public Builder jdbcType(JdbcType jdbcType) {
			resultMapping.jdbcType = jdbcType;
			return this;
		}
		/**
		 * 构建内部映射的ResultMapId
		 * @param nestedResultMapId
		 * @return
		 */
		public Builder nestedResultMapId(String nestedResultMapId) {
			resultMapping.nestedResultMapId = nestedResultMapId;
			return this;
		}
		/**
		 * 构建内查询ID
		 * @param nestedQueryId
		 * @return
		 */
		public Builder nestedQueryId(String nestedQueryId) {
			resultMapping.nestedQueryId = nestedQueryId;
			return this;
		}
		/**
		 * 构建对应元素的resultSet属性
		 * @param resultSet
		 * @return
		 */
		public Builder resultSet(String resultSet) {
			resultMapping.resultSet = resultSet;
			return this;
		}
		/**
		 * 构建对应元素的foreignColumn属性
		 * @param foreignColumn
		 * @return
		 */
		public Builder foreignColumn(String foreignColumn) {
			resultMapping.foreignColumn = foreignColumn;
			return this;
		}
		/**
		 * 构建对应元素的notNullColumn属性拆分后的结果
		 * @param notNullColumns
		 * @return
		 */
		public Builder notNullColumns(Set<String> notNullColumns) {
			resultMapping.notNullColumns = notNullColumns;
			return this;
		}
		/**
		 * 构建对应元素的columnPrefix属性
		 * @param columnPrefix
		 * @return
		 */
		public Builder columnPrefix(String columnPrefix) {
			resultMapping.columnPrefix = columnPrefix;
			return this;
		}
		/**
		 * 构建处理后的标志，标志共两个：id和constructor
		 * @param flags
		 * @return
		 */
		public Builder flags(List<ResultFlag> flags) {
			resultMapping.flags = flags;
			return this;
		}
		/**
		 * 构建类型处理器
		 * @param typeHandler
		 * @return
		 */
		public Builder typeHandler(TypeHandler<?> typeHandler) {
			resultMapping.typeHandler = typeHandler;
			return this;
		}
		/**
		 * 构建对应元素的column属性拆分后生成的结果，composites.size()>0会时column为null
		 * @param composites
		 * @return
		 */
		public Builder composites(List<ResultMapping> composites) {
			resultMapping.composites = composites;
			return this;
		}
		/**
		 * 构建延迟加载
		 * @param lazy
		 * @return
		 */
		public Builder lazy(boolean lazy) {
			resultMapping.lazy = lazy;
			return this;
		}
		/**
		 * 构建ResultMapping的flags,composites
		 * @return
		 */
		public ResultMapping build() {
			// lock down collections
			resultMapping.flags = Collections.unmodifiableList(resultMapping.flags);
			resultMapping.composites = Collections.unmodifiableList(resultMapping.composites);
			resolveTypeHandler();
			validate();
			return resultMapping;
		}

		/**
		 *  一些验证逻辑,验证result map有没有写错
		 */
		private void validate() {
			// Issue #697: cannot define both nestedQueryId and
			// nestedResultMapId
			if (resultMapping.nestedQueryId != null && resultMapping.nestedResultMapId != null) {
				throw new IllegalStateException(
						"Cannot define both nestedQueryId and nestedResultMapId in property " + resultMapping.property);
			}
			// Issue #5: there should be no mappings without typehandler
			if (resultMapping.nestedQueryId == null && resultMapping.nestedResultMapId == null
					&& resultMapping.typeHandler == null) {
				throw new IllegalStateException("No typehandler found for property " + resultMapping.property);
			}
			// Issue #4 and GH #39: column is optional only in nested resultmaps
			// but not in the rest
			if (resultMapping.nestedResultMapId == null && resultMapping.column == null
					&& resultMapping.composites.isEmpty()) {
				throw new IllegalStateException(
						"Mapping is missing column attribute for property " + resultMapping.property);
			}
			if (resultMapping.getResultSet() != null) {
				int numColums = 0;
				if (resultMapping.column != null) {
					numColums = resultMapping.column.split(",").length;
				}
				int numForeignColumns = 0;
				if (resultMapping.foreignColumn != null) {
					numForeignColumns = resultMapping.foreignColumn.split(",").length;
				}
				if (numColums != numForeignColumns) {
					throw new IllegalStateException(
							"There should be the same number of columns and foreignColumns in property "
									+ resultMapping.property);
				}
			}
		}
		/**
		 * 解析类型处理器
		 */
		private void resolveTypeHandler() {
			if (resultMapping.typeHandler == null && resultMapping.javaType != null) {
				Configuration configuration = resultMapping.configuration;
				TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
				resultMapping.typeHandler = typeHandlerRegistry.getTypeHandler(resultMapping.javaType,
						resultMapping.jdbcType);
			}
		}
		/**
		 * 构建结果集中的字段名
		 * @param column
		 * @return
		 */
		public Builder column(String column) {
			resultMapping.column = column;
			return this;
		}
	}

	/**
	 * 得到java对象的属性名
	 * @return
	 */
	public String getProperty() {
		return property;
	}

	/**
	 * 得到结果集中的字段名
	 * 
	 * @return String
	 */
	public String getColumn() {
		return column;
	}

	/**
	 * 得到属性类型
	 * 
	 * @return
	 */
	public Class<?> getJavaType() {
		return javaType;
	}

	/**
	 * 得到JDBC类型
	 * 
	 * @return
	 */
	public JdbcType getJdbcType() {
		return jdbcType;
	}

	/**
	 * 得到类型处理器
	 * 
	 * @return
	 */
	public TypeHandler<?> getTypeHandler() {
		return typeHandler;
	}

	/**
	 * 得到内部映射的ResultMapId
	 * 
	 * @return
	 */
	public String getNestedResultMapId() {
		return nestedResultMapId;
	}

	/**
	 * 得到内查询ID
	 * 
	 * @return
	 */
	public String getNestedQueryId() {
		return nestedQueryId;
	}

	/**
	 * 得到对应元素的notNullColumn属性拆分后的结果
	 * 
	 * @return
	 */
	public Set<String> getNotNullColumns() {
		return notNullColumns;
	}

	/**
	 * 得到对应元素的columnPrefix属性
	 * 
	 * @return
	 */
	public String getColumnPrefix() {
		return columnPrefix;
	}

	/**
	 * 得到处理后的标志，标志共两个：id和constructor
	 * 
	 * @return List<ResultFlag>
	 */
	public List<ResultFlag> getFlags() {
		return flags;
	}

	/**
	 * 得到对应元素的column属性拆分后生成的结果
	 * 
	 * @return
	 */
	public List<ResultMapping> getComposites() {
		return composites;
	}

	/**
	 * 判断对应元素的column属性拆分后生成的结果是否不为空
	 * 
	 * @return boolean
	 */
	public boolean isCompositeResult() {
		return this.composites != null && !this.composites.isEmpty();
	}

	/**
	 * 得到对应元素的resultSet属性
	 * 
	 * @return
	 */
	public String getResultSet() {
		return this.resultSet;
	}

	/**
	 * 得到对应元素的foreignColumn属性
	 * 
	 * @return
	 */
	public String getForeignColumn() {
		return foreignColumn;
	}

	/**
	 * 设置对应元素的foreignColumn属性
	 * 
	 * @param foreignColumn
	 */
	public void setForeignColumn(String foreignColumn) {
		this.foreignColumn = foreignColumn;
	}

	/**
	 * 是否延迟加载
	 * 
	 * @return
	 */
	public boolean isLazy() {
		return lazy;
	}

	/**
	 * 设置是否开启延迟加载
	 * 
	 * @param lazy
	 */
	public void setLazy(boolean lazy) {
		this.lazy = lazy;
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (o == null || getClass() != o.getClass()) {
			return false;
		}

		ResultMapping that = (ResultMapping) o;

		if (property == null || !property.equals(that.property)) {
			return false;
		}

		return true;
	}

	@Override
	public int hashCode() {
		if (property != null) {
			return property.hashCode();
		} else if (column != null) {
			return column.hashCode();
		} else {
			return 0;
		}
	}

}
